package com.senriot.quickup.service

import com.querydsl.core.BooleanBuilder
import com.querydsl.jpa.impl.JPAQueryFactory
import com.senriot.cloud.aliyun.model.EventPost
import com.senriot.cloud.common.AbstractService
import com.senriot.quickup.domain.Order
import com.senriot.quickup.domain.QOrder
import com.senriot.quickup.domain.QOrderBonus
import com.senriot.quickup.repository.OrderRepository
import com.senriot.quickup.security.ADMIN
import com.senriot.quickup.security.getCurrentUserLogin
import com.senriot.quickup.security.isCurrentUserInRole
import com.senriot.quickup.service.dto.OrderDTO
import com.senriot.quickup.service.dto.OrderStatistics
import com.senriot.quickup.service.dto.monthSales
import com.senriot.quickup.service.mapper.OrderMapper
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import reactor.event.Event
import reactor.function.Consumer
import java.math.BigDecimal
import java.time.LocalDate
import java.util.*
import javax.persistence.EntityManager

@Service
@Transactional
class OrderService(private val entityManager: EntityManager) : AbstractService<Order, Long, OrderDTO, OrderRepository, OrderMapper>(),
        Consumer<Event<EventPost>>
{

    private val queryFactory by lazy {
        JPAQueryFactory(entityManager)
    }

    /**
     * 出货事件
     * @param t Event<EventPost>
     */
    override fun accept(t: Event<EventPost>)
    {
        val event = t.data
        if (event.identifier == "deliverCommodityEvent")
        {
            /**
             * 修改出货状态
             */
            repository.findByOrdersId(event.value?.get("orderNo") as String).ifPresent {
                it.deliverStatus = event.value?.get("result") == 1
                repository.save(it)
            }
        }
    }

    fun statistics(): OrderStatistics
    {
        val b = BooleanBuilder()
        val order = QOrder.order
        val result = OrderStatistics()
        if (!isCurrentUserInRole(ADMIN))
        {
            b.and(order.device.store.agency.account.login.eq(getCurrentUserLogin().get()))
        }

//        val day = LocalDateTime.of(LocalDate.now())

        val total = queryFactory
                .select(order.amount.sum(), order.id.count())
                .from(order)
                .where(order.status.eq(1).and(order.isCashOut.isFalse).and(b))
                .fetchOne()

        result.totalSales = total?.get(0, BigDecimal::class.java) ?: BigDecimal(0)
        result.totalPayCount = total?.get(1, Long::class.java) ?: 0

        result.MonthlySales = queryFactory.select(order.createdDate.month(), order.amount.sum(), order.id.count())
                .from(order)
                .where(order.status.eq(1).and(order.isCashOut.isFalse).and(order.createdDate.year().goe(Calendar.getInstance().weekYear)))
                .groupBy(order.createdDate.month())
                .fetch()
                .map {
                    monthSales(it[0, Long::class.java].toString() + "月",
                            it.get(1, BigDecimal::class.java) ?: BigDecimal(0),
                            it.get(2, Long::class.java) ?: 0)
                }

        val dayTotal = queryFactory
                .selectFrom(order)
                .select(QOrder.order.amount.sum(), order.id.count())
                .where(order.createdDate.goe(LocalDate.now().atStartOfDay()).and(order.isCashOut.isFalse).and(b))
                .fetchOne()

        result.dailySales = dayTotal?.get(0, BigDecimal::class.java) ?: BigDecimal(0.00)
        result.dailyPayCount = dayTotal?.get(1, Long::class.java) ?: 0


        result.totalSalesVolume = queryFactory.select(order.id.count()).from(order)
                .where(order.deliverStatus.isTrue.and(b))
                .fetchOne()

        result.dailySalesVolume = queryFactory.select(order.id.count()).from(order)
                .where(order.createdDate.goe(LocalDate.now().atStartOfDay()).and(order.isCashOut.isFalse).and(order.deliverStatus.isTrue).and(
                        b))
                .fetchOne()

        log.info("total $total")
        log.info("dayTotal $dayTotal")
        return result
    }

    fun storeRanking(): MutableList<MutableMap<String, Any?>>
    {
        val p = BooleanBuilder()
        val order = QOrder.order
        if (!isCurrentUserInRole(ADMIN))
        {
            p.and(order.device.store.agency.account.login.eq(getCurrentUserLogin().get()))
        }
        val result = mutableListOf<MutableMap<String, Any?>>()
        queryFactory.select(order.device.store.name, order.amount.sum(), order.id.count())
                .from(order)
                .where(order.status.eq(1).and(order.isCashOut.isFalse).and(p))
                .groupBy(order.device.store.name)
                .orderBy(order.amount.sum().desc())
                .fetch().map {
                    val map = mutableMapOf<String, Any?>()
                    map["storeName"] = it[0, String::class.java]
                    map["sales"] = it[1, BigDecimal::class.java]
                    map["salesVolume"] = it[2, Long::class.java]
                    result.add(map)
                }

        return result
    }


    fun cashOutStatistics(): Map<String, BigDecimal?>
    {
        val order = QOrder.order
        val bonus = QOrderBonus.orderBonus
        val total = queryFactory.select(order.amount.sum()).from(order)
                .where(order.status.eq(1).and(order.isCashOut.isTrue))
                .fetchOne()

        val cashOut = queryFactory.select(bonus.amount.sum()).from(bonus)
                .where(bonus.order.status.eq(1))
                .fetchOne()

        return mapOf(Pair("total", total ?: BigDecimal(0.00)),
                Pair("surplus", total?.subtract(cashOut) ?: BigDecimal(0.00)))
    }

    fun orderStatistics(): Map<String, Any?>
    {
        val order = QOrder.order
        val query = order.status.eq(1).and(order.isCashOut.isFalse)
        val total = queryFactory.select(order.amount.sum()).from(order)
                .where(query)
                .fetchOne()

        val count = queryFactory.selectFrom(order)
                .where(query.and(order.deliverStatus.isTrue))
                .fetchCount()

        return mapOf(Pair("count", count),
                Pair("total", total ?: BigDecimal(0.00)),
                Pair("profit", total?.divide(BigDecimal(3), 2, BigDecimal.ROUND_HALF_UP) ?: BigDecimal(0.00)))
    }
}
